package com.dreamers.pathcore;


import com.dreamers.map.MapArray;

import java.util.*;

public class AStarPriorityQueue {

    private Queue<AStarTransform> openList;
    private Queue<AStarTransform> closeList;
    AStarTransform startPoint = null;
    AStarTransform endPoint = null;
    boolean isFound = true;

    boolean isLog = false;

    public void setStartPoint(int x, int y) {
        startPoint = new AStarTransform();
        startPoint.setPosition(new Vector2(x, y));
        startPoint.setH(0);
        startPoint.setaStarTransformParent(null);
    }

    public void setEndPoint(int x, int y) {
        endPoint = new AStarTransform();
        endPoint.setPosition(new Vector2(x, y));
    }

    void start() {
        isFound = true;
        openList = new PriorityQueue<>();
        closeList = new PriorityQueue<AStarTransform>();

    }

    private int count = 0;

    public List<AStarTransform> AStarRun() {
        start();
        openList.add(startPoint);
        while (getGValue(startPoint.getPosition(), endPoint.getPosition()) != 0) {
            count++;
            if (isLog) {
                System.out.println("count:" + count);
            }
            if (startPoint.getPosition().getX() + 1 < MapArray.map[0].length
                    && (MapArray.map[startPoint.getPosition().getY()][startPoint.getPosition().getX() + 1] == 1
                    || MapArray.map[startPoint.getPosition().getY()][startPoint.getPosition().getX() + 1] == 5)
                    && notInList(startPoint, closeList, 1, 0)
                    && notInList(startPoint, openList, 1, 0)) {
                AStarTransform tempPoint = getaStarStruct(startPoint, endPoint, startPoint.getPosition().getX() + 1, startPoint.getPosition().getY());
                openList.add(tempPoint);

                if (isLog) {
                    LogUtil.INSTANCE.info("[startPoint.getPosition().getY()][startPoint.getX()+1]");
                }

            }
            if (startPoint.getPosition().getX() - 1 >= 0
                    && (MapArray.map[startPoint.getPosition().getY()][startPoint.getPosition().getX() -  1] == 1
                    || MapArray.map[startPoint.getPosition().getY()][startPoint.getPosition().getX() -  1] == 5)
                    && notInList(startPoint, closeList, -1, 0)
                    && notInList(startPoint, openList, -1, 0)) {
                AStarTransform tempPoint = getaStarStruct(startPoint, endPoint, startPoint.getPosition().getX() - 1, startPoint.getPosition().getY());
                openList.add(tempPoint);
                if (isLog) {
                    LogUtil.INSTANCE.info("[startPoint.getPosition().getY()][startPoint.getX()-1]");
                }

            }
            if (startPoint.getPosition().getY() + 1 < MapArray.map.length
                    && (MapArray.map[startPoint.getPosition().getY()+ 1][startPoint.getPosition().getX() ] == 1
                    || MapArray.map[startPoint.getPosition().getY()+ 1][startPoint.getPosition().getX() ] == 5)
                    && notInList(startPoint, closeList, 0, 1)
                    && notInList(startPoint, openList, 0, 1)) {
                AStarTransform tempPoint = getaStarStruct(startPoint, endPoint, startPoint.getPosition().getX(), startPoint.getPosition().getY() + 1);
                openList.add(tempPoint);
                if (isLog) {
                    LogUtil.INSTANCE.info("[startPoint.getPosition().getY()+1][startPoint.getX()]");
                }
            }
            if (startPoint.getPosition().getY() - 1 >= 0
                    && (MapArray.map[startPoint.getPosition().getY()-1][startPoint.getPosition().getX() ] == 1
                    || MapArray.map[startPoint.getPosition().getY()- 1][startPoint.getPosition().getX() ] == 5)
                    && notInList(startPoint, closeList, 0, -1)
                    && notInList(startPoint, openList, 0, -1)) {
                AStarTransform tempPoint = getaStarStruct(startPoint, endPoint, startPoint.getPosition().getX(), startPoint.getPosition().getY() - 1);
                openList.add(tempPoint);
                if (isLog) {
                    LogUtil.INSTANCE.info("[startPoint.getPosition().getY()-1][startPoint.getX()]");
                }
            }
            closeList.add(startPoint);
            openList.remove(startPoint);
            if (openList.size() == 0) {
                isFound = false;
                if (isLog) {
                    LogUtil.INSTANCE.info("not find path");
                }
                break;
            }
            startPoint = getNewStartPoint();

            if (isLog) {

                LogUtil.INSTANCE.info(startPoint.toString());
            }
        }
        List<AStarTransform> resultStack = getResultStack(startPoint);
        if (resultStack!=null){
            Collections.reverse(resultStack);
            return resultStack;
        }
        return null;
    }

    private int getGValue(Vector2 p1, Vector2 p2) {
        return Math.abs(p2.getX() - p1.getX()) + Math.abs(p2.getY() - p1.getY());
    }

    private List<AStarTransform> getResultStack(AStarTransform startPoint) {
        if (isFound == false)
            return null;
        List<AStarTransform> aStarTransformList = new ArrayList<>();
        while (startPoint.getaStarTransformParent() != null) {
            for (AStarTransform aStarTransform : closeList) {
                if (aStarTransform.getPosition().getX() == startPoint.getaStarTransformParent().getPosition().getX() &&
                        aStarTransform.getPosition().getY() == startPoint.getaStarTransformParent().getPosition().getY()) {
                    if (isLog) {
                        LogUtil.INSTANCE.info("openList" + startPoint.toString());
                    }
                    aStarTransformList.add(startPoint);
                    startPoint = aStarTransform;
                    break;
                }
            }
        }
        return aStarTransformList;
    }

    private AStarTransform getNewStartPoint() {
        return openList.poll();
    }


    private AStarTransform getaStarStruct(AStarTransform startPoint, AStarTransform endPoint, int x, int y) {
        AStarTransform tempPoint = new AStarTransform();
        tempPoint.setPosition(new Vector2(x, y));
        tempPoint.setH(startPoint.getH() + 1);
        tempPoint.setaStarTransformParent(startPoint);
        tempPoint.setG(getGValue(tempPoint.getPosition().getX(), tempPoint.getPosition().getY(), endPoint.getPosition().getX(), endPoint.getPosition().getY()));
        tempPoint.setF(tempPoint.getH() + tempPoint.getG());
        return tempPoint;
    }


    private int getGValue(int startX, int startY, int endX, int endY) {
        return Math.abs(endX - startX) + Math.abs(endY - startY);
    }

    private boolean notInList(AStarTransform aStarTransform, Queue<AStarTransform> aStarTransformLinkedList, int xoffset, int yoffset) {
        boolean notInCloseList = true;
        for (AStarTransform starTransform : aStarTransformLinkedList) {
            if (starTransform.getPosition().getX() == (aStarTransform.getPosition().getX() + xoffset) &&
                    starTransform.getPosition().getY() == (aStarTransform.getPosition().getY() + yoffset)) {
                notInCloseList = false;
            }
        }
        return notInCloseList;
    }
}
